package com.such.kit.placeholder;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import com.such.kit.string.StringCommons;
import com.such.kit.validate.ValidateCommons;

/**
 * @author SUCH
 * <pre>
 * 字符串占位符替操作工具类
 * 设计初衷是希望可以方便的将字符串中的占位符替换为指定值
 * 
 * PlaceholderCommons 提供了 4 类工具
 *   检查字符串中是否存在占位符 {@link #hasExpr(String, String)}
 *   获取字符串中的占位符 {@link #getKeys(String, String, int, int)}
 *   匹配、替换字符串中的占位符 {@link #replacePlaceholder(String, String, int, int, Map, boolean)}
 *   反转解析字符串 {@link #reversalPlaceholder(String, String, int, int, String)}
 * 
 * PlaceholderCommons 的默认正则表达式规则为 {@link #innerExpr}
 * </pre>
 */
public class PlaceholderCommons {

	public static final String PLACEHOLDER_PREFIX = "#[";
	public static final String PLACEHOLDER_SUFFIX = "]";

	/** innerExpr：默认的正则表达式规则，匹配格式为 #[key] */
	private static String innerExpr = "\\#\\[[0-9a-zA-Z\\$\\.\\-\\_]+\\]";

	/**
	 * <pre>
	 * [参考 {@link #hasExpr(String, String)}]
	 * 
	 * 使用默认的 {@link #innerExpr 正则表达式规则} 匹配
	 * </pre>
	 */
	public static boolean hasInnerExpr(String str) {
		return hasExpr(innerExpr, str);
	}

	/**
	 * <pre>
	 * [参考 {@link #getInnerKeys(String)}]
	 * 
	 * 取占位符集中的第 1 个占位符
	 * </pre>
	 */
	public static String getInnerKey(String str) {
		List<String> innerKeys = getInnerKeys(str);
		if (ValidateCommons.isNotEmpty(innerKeys)) {
			return innerKeys.get(0);
		}
		return null;
	}

	/**
	 * <pre>
	 * [参考 {@link #getKeys(String, String, int, int)}]
	 * 
	 * 使用默认的 {@link #innerExpr 正则表达式规则} 匹配，并截去 #[、] 作为 key
	 * </pre>
	 */
	public static List<String> getInnerKeys(String str) {
		return getKeys(innerExpr, str, PLACEHOLDER_PREFIX.length(), PLACEHOLDER_SUFFIX.length());
	}

	/**
	 * <pre>
	 * 根据正则表达式规则，匹配待替换的字符串中是否存在占位符
	 * </pre>
	 * @param expr 正则表达式规则
	 * @param str 待替换的字符串
	 * @return 是否存在 [true 存在 | false 不存在]
	 */
	public static boolean hasExpr(String expr, String str) {
		return ValidateCommons.isNotEmpty(getKeys(expr, str, 0, 0));
	}

	/**
	 * <pre>
	 * [参考 {@link #getKeys(String, String, int, int)}]
	 * 
	 * 取占位符集中的第 1 个占位符
	 * </pre>
	 */
	public static String getKey(String expr, String str, int cutPrefix, int cutSuffix) {
		List<String> innerKeys = getKeys(expr, str, cutPrefix, cutSuffix);
		if (ValidateCommons.isNotEmpty(innerKeys)) {
			return innerKeys.get(0);
		}
		return null;
	}

	/**
	 * <pre>
	 * 根据正则表达式规则，匹配待替换的字符串中的占位符
	 * </pre>
	 * @param expr 正则表达式规则
	 * @param str 待替换的字符串
	 * @param cutPrefix 截取占位符开头位
	 * @param cutSuffix 截取占位符末尾位
	 * @return 占位符集
	 */
	public static List<String> getKeys(String expr, String str, int cutPrefix, int cutSuffix) {
		List<String> innerKeys = new ArrayList<String>();
		Pattern keyPattern = Pattern.compile(expr);
		Matcher keyMatcher = keyPattern.matcher(str);
		while (keyMatcher.find()) {
			String key = keyMatcher.group();
			String innerKey = key.substring(cutPrefix, key.length() - cutSuffix).trim();
			innerKeys.add(innerKey);
		}
		return innerKeys;
	}

	/**
	 * <pre>
	 * [参考 {@link #replacePlaceholder(String, String, int, int, Map, boolean)}]
	 * 
	 * 使用默认的 {@link #innerExpr 正则表达式规则} 匹配，并截去 #[、] 作为 key，同时忽略占位符大小写
	 * </pre>
	 */
	public static String replacePlaceholder(String str, Map<String, ?> params) {
		return replacePlaceholder(str, params, true);
	}

	/**
	 * <pre>
	 * [参考 {@link #replacePlaceholder(String, String, int, int, Map, boolean)}]
	 * 
	 * 使用默认的 {@link #innerExpr 正则表达式规则} 匹配，并截去 #[、] 作为 key
	 * </pre>
	 */
	public static String replacePlaceholder(String str, Map<String, ?> params, boolean caseInsensitive) {
		return replacePlaceholder(innerExpr, str, PLACEHOLDER_PREFIX.length(), PLACEHOLDER_SUFFIX.length(), params, caseInsensitive);
	}

	/**
	 * <pre>
	 * 根据正则表达式规则，匹配待替换的字符串中的占位符，将其替换为参数集中对应 key 的值
	 * </pre>
	 * @param expr 正则表达式规则
	 * @param str 待替换的字符串
	 * @param cutPrefix 截取占位符开头位
	 * @param cutSuffix 截取占位符末尾位
	 * @param params 参数集
	 * @param caseInsensitive 是否忽略占位符大小写 [true 忽略 | false 不忽略]
	 * @return 替换占位符后的字符串
	 */
	public static String replacePlaceholder(String expr, String str, int cutPrefix, int cutSuffix, Map<String, ?> params, boolean caseInsensitive) {
		if (ValidateCommons.isEmpty(str)) {
			return str;
		}
		if (params == null) {
			return str;
		}
		if (caseInsensitive && params.size() > 0) {
			Map<String, Object> caseInsensitiveMap = new HashMap<String, Object>();
			for (String key : params.keySet()) {
				caseInsensitiveMap.put(StringCommons.toUpperCase(key), params.get(key));
			}
			params = caseInsensitiveMap;
		}

		Pattern keyPattern = Pattern.compile(expr);
		Matcher keyMatcher = keyPattern.matcher(str);
		List<String> contents = Arrays.stream(keyPattern.split(str)).collect(Collectors.toList());
		List<String> values = new ArrayList<String>();
		while (keyMatcher.find()) {
			String key = keyMatcher.group();
			String innerKey = key.substring(cutPrefix, key.length() - cutSuffix).trim();
			if (caseInsensitive) {
				innerKey = StringCommons.toUpperCase(innerKey);
			}
			Object innerValue = params.get(innerKey);
			if (ValidateCommons.isNotEmpty(innerValue, false)) {
				String value = innerValue.toString();

				values.add(value);
			} else {
				values.add("");
			}
		}
		int contentSize = contents.size();
		int valueSize = values.size();
		int maxSize = (contentSize > valueSize) ? contentSize : valueSize;
		StringBuffer replaceStr = new StringBuffer();
		for (int i = 0; i < maxSize; i++) {
			if (i < contentSize) {
				replaceStr.append(contents.get(i));
			}
			if (i < valueSize) {
				replaceStr.append(values.get(i));
			}
		}
		return replaceStr.toString();
	}

	/**
	 * <pre>
	 * [参考 {@link #reversalPlaceholder(String, String, int, int, String)}]
	 * 
	 * 使用默认的 {@link #innerExpr 正则表达式规则} 匹配，并截去 #[、] 作为 key
	 * </pre>
	 */
	public static Map<String, String> reversalPlaceholder(String rule, String str) {
		return reversalPlaceholder(innerExpr, rule, 2, 1, str);
	}

	/**
	 * <pre>
	 * 根据正则表达式规则，匹配占位符规则中的占位符，反转解析字符串，将字符串解析成占位符与值的映射关系
	 * 
	 * [警告] 此方法使用范围较窄，只能解析有明确分隔规则的字符串
	 * [举例]
	 *   适用于 #[F1]_#[F2]>>#[F3]$$$#[F4]，此字符串有明确分隔规则，如 _、>>、$$$
	 *   不适用 #[F1]#[F2]#[F3]#[F4]
	 * </pre>
	 * @param expr 正则表达式规则
	 * @param rule 占位符规则
	 * @param cutPrefix 截取占位符开头位
	 * @param cutSuffix 截取占位符末尾位
	 * @param str 待反转解析的字符串
	 * @return 占位符与值的映射关系
	 */
	public static Map<String, String> reversalPlaceholder(String expr, String rule, int cutPrefix, int cutSuffix, String str) {
		Pattern keyPattern = Pattern.compile(expr);
		Matcher keyMatcher = keyPattern.matcher(rule);
		List<String> separates = new ArrayList<String>();
		List<String> innerKeys = new ArrayList<String>();
		Map<String, String> params = new HashMap<String, String>();
		for (String separate : keyPattern.split(rule)) {
			if (ValidateCommons.isNotEmpty(separate)) {
				separates.add(separate);
			}
		}
		while (keyMatcher.find()) {
			String key = keyMatcher.group();
			String innerKey = key.substring(cutPrefix, key.length() - cutSuffix).trim();
			innerKeys.add(innerKey);
		}
		for (int i = 0; i < separates.size(); i++) {
			String separate = separates.get(i);
			int index = str.indexOf(separate);
			if (index > -1 && innerKeys.size() > 0) {
				String value = str.substring(0, index);
				params.put(innerKeys.remove(0), value);
			}
			str = str.substring(index + separate.length());
		}
		return params;
	}
	
}
